package registration.servlet;

import static org.junit.Assert.*;

import java.net.HttpURLConnection;
import java.util.*;

import javax.ws.rs.core.MediaType;

import msg.*;
import msg.servlet.MessageResourceTest;

import org.apache.wink.client.*;
import org.json.*;
import org.junit.*;
import org.junit.Test;

import registration.RegistrationManager;
import user.*;
import user.servlet.UsersResource;

import common.Pair;
import common.dal.Transaction;

@SuppressWarnings({"javadoc", "static-method"})
public class EventResourceTest extends MessageResourceTest {
	static String	EVENTS;
	
	@BeforeClass
	public static void setupBeforeClass() throws Exception {
		MessageResourceTest.setupBeforeClass();
		EVENTS = getResources() + "events";
	}
	
	@Override
	@Test
	public void testPost() throws Exception {
		Resource res = rc.resource(EVENTS).contentType(MediaType.APPLICATION_JSON);
		JSONObject request = getDefaultRequest();
		// post with bad cookie
		ClientResponse response = res.header("Cookie", UsersResource.COOKIE_NAME + "=101").post(request);
		assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, response.getStatusCode());
		// post with real cookie
		res = res.cookie(createCookie(cookie));
		response = res.post(request);
		assertEquals(HttpURLConnection.HTTP_CREATED, response.getStatusCode());
		assertEquals(EVENTS + "/0", response.getHeaders().getFirst("Location"));
		// post with bad capacity
		request.put("capacity", -1);
		response = res.post(request);
		assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, response.getStatusCode());
		// post with time over
		request.put("capacity", 10);
		request.put("time", Calendar.getInstance().getTimeInMillis());
		Thread.sleep(10);
		response = res.post(request);
		assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, response.getStatusCode());
	}
	
	@Override
	@Test
	public void testRetrieve() throws Exception {
		// check non-existing resource
		Resource res = rc.resource(EVENTS + "/101");
		ClientResponse response = res.get();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// int in order to pass tests
		Pair<Long, Long> eventPair = postEvent();
		long id = eventPair.first;
		// check existing resource
		response = rc.resource(EVENTS + "/" + id).get();
		JSONObject responseObject = response.getEntity(JSONObject.class);
		JSONObject request = getDefaultRequest();
		request.put("time", eventPair.second);
		request.put("id", id);
		request.put("poster", getUser().getId());
		assertEquals(request, responseObject);
		// check existing messsage
		Long eventId = postMessage();
		response = rc.resource(EVENTS + "/" + eventId).get();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
	}
	
	@Override
	@Test
	public void testList() throws Exception {
		// check non-existing resource
		Resource res = rc.resource(EVENTS);
		ClientResponse response = res.get();
		JSONArray array = new JSONArray();
		assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode());
		assertEquals(array, response.getEntity(JSONArray.class));
		Pair<Long, Long> eventPair = postEvent();
		long id = eventPair.first;
		// check existing resource
		response = rc.resource(EVENTS).get();
		JSONArray responseObject = response.getEntity(JSONArray.class);
		JSONObject request = getDefaultRequest();
		request.put("id", id);
		request.put("time", eventPair.second);
		// make sure messages aren't counted
		postMessage();
		response = rc.resource(EVENTS).get();
		array.put(request);
		assertEquals(array, responseObject);
	}
	
	@Test
	public void testListRegistered() throws Exception {
		// check non-existing resource
		Resource res = rc.resource(EVENTS + "/101/registered");
		ClientResponse response = res.get();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		JSONArray array = new JSONArray();
		Pair<Long, Long> eventPair = postEvent();
		long id = eventPair.first;
		// check existing resource
		response = rc.resource(EVENTS).get();
		JSONArray responseObject = response.getEntity(JSONArray.class);
		JSONObject request = getDefaultRequest();
		request.put("id", id);
		request.put("time", eventPair.second);
		// make sure messages aren't counted
		postMessage();
		response = rc.resource(EVENTS).get();
		array.put(request);
		assertEquals(array, responseObject);
		// try to register to message
	}
	
	@Override
	@Test
	public void testDelete() throws Exception {
		// delete non existing cookie
		Resource res = rc.resource(EVENTS + "/101");
		// delete with no cookie
		ClientResponse response = res.delete();
		assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, response.getStatusCode());
		// delete non-existing
		response = res.cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_NO_CONTENT, response.getStatusCode());
		// delete using non-existing cookie
		long id = postEvent().first;
		res = rc.resource(EVENTS + "/" + id);
		response = res.header("Cookie", UsersResource.COOKIE_NAME + "=" + cookie + "!").delete();
		assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, response.getStatusCode());
		// delete using wrong cookie
		t = new Transaction();
		LoginManager.getNewInstance(t).registerUser(getFakeUser().getLogin(), "whatever", PASSWORD);
		long newCookie = LoginManager.getNewInstance(t).loginUser(getFakeUser().getLogin(), PASSWORD).getId();
		t.commit();
		response = res.header("Cookie", UsersResource.COOKIE_NAME + "=" + newCookie).delete();
		assertEquals(HttpURLConnection.HTTP_FORBIDDEN, response.getStatusCode());
		// delete using correct cookie
		response = res.cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode());
		// assure is deleted
		response = res.delete();
		assertEquals(HttpURLConnection.HTTP_NO_CONTENT, response.getStatusCode());
		response = res.get();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// can't delete messages
		Long eventId = postMessage();
		response = rc.resource(EVENTS + "/" + eventId).cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// can't delete old events
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MILLISECOND, 100);
		t = new Transaction();
		EventMessage event = MessageManager.getNewInstance(t)
				.createNewEventMessage(new Location(0.5, 1), "event to be deleted", getUser(), cal, 1);
		t.commit();
		Thread.sleep(100);
		response = rc.resource(EVENTS + "/" + event.getId()).cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_FORBIDDEN, response.getStatusCode());
	}
	
	@Test
	public void testRegister() throws Exception {
		
		// test non-existing resource
		ClientResponse response = rc.resource(EVENTS + "/101/register").cookie(createCookie(cookie)).post(null);
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// test with bad cookie
		long eventId = postEvent().first;
		Resource res = rc.resource(EVENTS + "/" + eventId + "/register");
		response = res.header("Cookie", UsersResource.COOKIE_NAME + "=" + cookie + "1").post(null);
		assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, response.getStatusCode());
		// test with good cookie and resource
		res = res.cookie(createCookie(cookie));
		response = res.post(null);
		assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode());
		response = res.post(null);
		assertEquals(HttpURLConnection.HTTP_NO_CONTENT, response.getStatusCode());
		// try to register to message
		long msgId = postMessage();
		response = rc.resource(EVENTS + "/" + msgId + "/register").cookie(createCookie(cookie)).post(null);
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// check is registered
		response = rc.resource(EVENTS + "/" + eventId + "/registered").get();
		JSONArray expected = new JSONArray();
		expected.put(common.servlet.AbstractResource.userToJSON(getUser()));
		assertEquals(expected, response.getEntity(JSONArray.class));
	}
	
	@Test
	public void testRegisterFull() throws Exception {
		t = new Transaction();
		User user = LoginManager.getNewInstance(t).registerUser("b" + new Date().getTime(), "gal", PASSWORD);
		EventMessage event = MessageManager.getNewInstance(t)
				.createNewEventMessage(new Location(0, 0), "event", getUser(), getTomorrow(), 1);
		RegistrationManager.getNewInstance(t).registerUserForEvent(user, event);
		t.commit();
		ClientResponse response = rc.resource(EVENTS + "/" + event.getId() + "/register")
				.cookie(createCookie(cookie)).post(null);
		assertEquals(HttpURLConnection.HTTP_FORBIDDEN, response.getStatusCode());
	}
	
	@Test
	public void testUnregister() throws Exception {
		long msgId = 0;
		ClientResponse response = rc.resource(EVENTS + "/101/register").cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// test with bad cookie
		long id = postEvent().first;
		Resource res = rc.resource(EVENTS + "/" + id + "/register");
		response = res.header("Cookie", UsersResource.COOKIE_NAME + "=" + cookie + "1").delete();
		assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, response.getStatusCode());
		// test with good cookie and resource, not yet registered
		res = res.cookie(createCookie(cookie));
		response = res.delete();
		assertEquals(HttpURLConnection.HTTP_NO_CONTENT, response.getStatusCode());
		// register then unregister
		response = rc.resource(EVENTS + "/" + id + "/register").cookie(createCookie(cookie)).post(null);
		assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode());
		response = res.delete();
		assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode());
		// try to unregister from a message
		msgId = postMessage();
		response = rc.resource(EVENTS + "/" + msgId + "/register").cookie(createCookie(cookie)).delete();
		assertEquals(HttpURLConnection.HTTP_NOT_FOUND, response.getStatusCode());
		// make sure is no longer registered
		response = rc.resource(EVENTS + "/" + id + "/registered").get();
		assertEquals(new JSONArray(), response.getEntity(JSONArray.class));
	}
	
	@Override
	public void testRadius() throws Exception {
		// test empty
		Resource res = rc.resource(EVENTS + getInRadiusString());
		assertEquals(new JSONArray(), res.get().getEntity(JSONArray.class));
		// add a new msg
		t = new Transaction();
		MessageManager.getNewInstance(t).createNewMessage(netanya, "msg1", getUser());
		// add new events
		Message haifaMsg = MessageManager.getNewInstance(t)
				.createNewEventMessage(haifa, "event", getUser(), getTomorrow(), 10);
		Message taMsg = MessageManager.getNewInstance(t)
				.createNewEventMessage(telAviv, "event", getUser(), getTomorrow(), 10);
		t.commit();
		res = rc.resource(EVENTS
				+ getInRadiusString(netanya, new Location(netanya.getLatitude() - 0.05,
						netanya.getLongitude() - 0.05), new Location(netanya.getLatitude() + 0.05, netanya
						.getLongitude() + 0.05)));
		assertEquals(new JSONArray(), res.get().getEntity(JSONArray.class));
		res = rc.resource(EVENTS
				+ getInRadiusString(haifa, new Location(haifa.getLatitude() - 0.05, haifa.getLongitude() - 0.05), new Location(
						haifa.getLatitude() + 0.05, haifa.getLongitude() + 0.05)));
		assertEquals(new JSONArray(Arrays.asList(haifaMsg.accept(jsoner))), res.get().getEntity(JSONArray.class));
		res = rc.resource(EVENTS
				+ getInRadiusString(netanya, new Location(netanya.getLatitude() - 0.5,
						netanya.getLongitude() - 0.5), new Location(netanya.getLatitude() + 0.5, netanya
						.getLongitude() + 0.5)));
		res = rc.resource(EVENTS + getInRadiusString(netanya, haifa, haifa));
		assertEquals(new JSONArray(Arrays.asList(haifaMsg.accept(jsoner), taMsg.accept(jsoner))), res.get()
				.getEntity(JSONArray.class));
	}
	
	@Override
	@Test
	public void testAddressSaved() throws Exception {
		Resource res = rc.resource(EVENTS).cookie(createCookie(cookie));
		JSONObject request = getDefaultRequest();
		request.getJSONObject("location").put("address", "Internationale");
		ClientResponse response = res.post(request);
		assertEquals(HttpURLConnection.HTTP_CREATED, response.getStatusCode());
		
		response = rc.resource(response.getHeaders().getFirst("location")).get();
		assertEquals(request, response.getEntity(JSONObject.class).remove("id"));
	}
	
	@Override
	@Test
	public void testSince() throws Exception {
		t = new Transaction();
		Message event = MessageManager.getNewInstance(t)
				.createNewEventMessage(new Location(0.5, 0.5), "oldMessage", getUser(), getTomorrow(), 10);
		Calendar firstMsgTime = Calendar.getInstance();
		firstMsgTime.setTimeInMillis(event.getTime());
		Thread.sleep(10);
		Message newEvent = MessageManager.getNewInstance(t)
				.createNewEventMessage(new Location(0.5, 0.5), "newMessage", getUser(), getTomorrow(), 10);
		MessageManager.getNewInstance(t).createNewMessage(event.getLocation(), "event", getUser());
		t.commit();
		long since = firstMsgTime.getTimeInMillis() + 5;
		Resource res = rc.resource(EVENTS + getInRadiusString() + "&since=" + since);
		assertEquals(new JSONArray(Arrays.asList(newEvent.accept(jsoner))), res.get().getEntity(JSONArray.class));
		since = firstMsgTime.getTimeInMillis();
		res = rc.resource(EVENTS + getInRadiusString() + "&since=" + since);
		assertEquals(new JSONArray(Arrays.asList(event.accept(jsoner), newEvent.accept(jsoner))), res.get()
				.getEntity(JSONArray.class));
		Thread.sleep(5);
		res = rc.resource(EVENTS + getInRadiusString() + "&since=" + getTomorrow().getTimeInMillis());
		assertEquals(new JSONArray(), res.get().getEntity(JSONArray.class));
	}
	
	@Test
	public void testPast() throws Exception {
		t = new Transaction();
		Message event = MessageManager
				.getNewInstance(t)
				.createNewEventMessage(new Location(10.5, 10.5), "oldMessage", getUser(), new Date().getTime() + 10, 10);
		Thread.sleep(10);
		Message newEvent = MessageManager.getNewInstance(t)
				.createNewEventMessage(new Location(10.5, 10.5), "newMessage", getUser(), getTomorrow(), 10);
		t.commit();
		Resource res = rc.resource(EVENTS + "/inRadius?lat=10.5&long=10.5&r=0&past=" + true);
		assertEquals(new JSONArray(Arrays.asList(event.accept(jsoner), newEvent.accept(jsoner))), res.get()
				.getEntity(JSONArray.class));
		res = rc.resource(EVENTS + "/inRadius?lat=10.5&long=10.5&r=0&past=" + false);
		assertEquals(new JSONArray(Arrays.asList(newEvent.accept(jsoner))), res.get().getEntity(JSONArray.class));
		res = rc.resource(EVENTS + "/inRadius?lat=10.5&long=10.5&r=0&past=" + true + "&since="
				+ newEvent.getTime());
		assertEquals(new JSONArray(Arrays.asList(newEvent.accept(jsoner))), res.get().getEntity(JSONArray.class));
	}
	
	@Override
	protected JSONObject getDefaultRequest() {
		JSONObject request = super.getDefaultRequest();
		request.put("content", "new event");
		request.put("capacity", 10);
		request.put("time", getTomorrow().getTimeInMillis());
		return request;
	}
}
